package express.before;

import express.IExpressCalculate;
import express.constant.OperatorEnum;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 前缀表达式计算
 *
 * @author miweidong
 * @date 2021-08-10 15:57
 */
public class BeforeExpressCalculate implements IExpressCalculate {

    /**
     * 生成前缀表达式
     *
     * @param words 单词集
     * @return 前缀表达式
     */
    public List<String> generalBeforeSerializer(List<String> words) {
        if (words.size() == 0) {
            return null;
        }
        SYMBOLSTACK.clear();
        List<String> beforeWords = new ArrayList<>();
        Collections.reverse(words);
        words.add("=");
        for (String word : words) {
            OperatorEnum operatorEnum = OperatorEnum.getOperatorEnumBySymbol(word);
            if (isNumber(operatorEnum)) {
                beforeWords.add(word);
            } else {
                while (!comparePrimary(operatorEnum) && !SYMBOLSTACK.empty()) {
                    beforeWords.add(SYMBOLSTACK.pop());
                }
                if (!OperatorEnum.EQUALS.equals(operatorEnum)) {
                    SYMBOLSTACK.push(word);
                    if (OperatorEnum.LEFT_PARENTHESES.equals(operatorEnum)) {
                        SYMBOLSTACK.pop();
                        SYMBOLSTACK.pop();
                    }
                }
            }
        }
        return beforeWords;
    }

    /**
     * 前缀表达式计算
     *
     * @param wordLists 单词集
     * @return 计算结果
     */
    @Override
    public BigDecimal calculate(List<String> wordLists) {
        List<String> words = generalBeforeSerializer(wordLists);
        if (words.size() == 0) {
            return null;
        }
        NUMBERSTACK.clear();
        for (String word : words) {
            OperatorEnum operatorEnum = OperatorEnum.getOperatorEnumBySymbol(word);
            if (isNumber(operatorEnum)) {
                NUMBERSTACK.push(new BigDecimal(word));
            } else {
                BigDecimal a = NUMBERSTACK.pop();
                BigDecimal b = NUMBERSTACK.pop();
                mathematical(a, b, operatorEnum);
            }
        }
        return NUMBERSTACK.pop();
    }

    /**
     * 优先级比较
     *
     * @param symbol 运算符
     * @return 比较结果
     */
    @Override
    public Boolean comparePrimary(OperatorEnum symbol) {
        if (SYMBOLSTACK.empty()) {
            return true;
        }
        String top = SYMBOLSTACK.peek();
        OperatorEnum operatorTopSymbol = OperatorEnum.getOperatorEnumBySymbol(top);
        if (OperatorEnum.RIGHT_PARENTHESES.equals(operatorTopSymbol)) {
            return true;
        }
        switch (symbol) {
            case RIGHT_PARENTHESES:
            case MULTIPLY:
            case DIVIDE:
                return true;
            case ADD:
            case SUBTRACT:
                return OperatorEnum.ADD.equals(operatorTopSymbol) || OperatorEnum.SUBTRACT.equals(operatorTopSymbol);
            case LEFT_PARENTHESES:
            case EQUALS:
                return false;
            default:
                break;
        }
        return true;
    }

}
